package znet

import (
	"errors"
	"fmt"
	"io"
	"net"
	"sync"
	"zinx-new/utils"
	"zinx-new/ziface"
)

type Connection struct {
	// TCP 套接字
	Conn *net.TCPConn
	// server
	TcpServer ziface.IServer
	// 当前连接id，也可以称为session id， 全局唯一
	ConnID   uint32
	isClosed bool
	// api
	handleAPI ziface.HandFunc
	// 告知链接已经退出
	ExitBuffChan chan bool
	// 处理router
	MsgHandle ziface.IMsgHandle
	// 读写管道
	msgChan chan []byte
	// 带缓冲的管道
	msgBuffChan chan []byte

	// 属性
	property     map[string]interface{}
	propertyLock sync.RWMutex
}

func NewConnection(conn *net.TCPConn, connID uint32, callback ziface.HandFunc, handle ziface.IMsgHandle, server ziface.IServer) *Connection {
	c := &Connection{
		Conn:         conn,
		TcpServer:    server,
		ConnID:       connID,
		isClosed:     false,
		handleAPI:    callback,
		ExitBuffChan: make(chan bool, 1),
		MsgHandle:    handle,
		msgChan:      make(chan []byte),
		msgBuffChan:  make(chan []byte, utils.GlobalObject.MaxMsgChanLen),
		property:     make(map[string]interface{}),
	}
	// 将新创建的Conn添加到链接管理中
	c.TcpServer.GetConnMgr().Add(c)
	return c
}

// 处理数据读取
func (c *Connection) StartReader() {
	fmt.Println("Reader Goroutine is running")
	defer fmt.Println(c.RemoteAddr().String(), " conn reader exit!")
	defer c.Stop()

	for {
		dp := NewDataPack()
		headData := make([]byte, dp.GetHeadLen())
		if _, err := io.ReadFull(c.GetTCPConnection(), headData); err != nil {
			fmt.Println("read msg head error ", err)
			c.ExitBuffChan <- true
			continue
		}

		// 拆包
		msg, err := dp.Unpack(headData)
		if err != nil {
			fmt.Println("unpack error ", err)
			c.ExitBuffChan <- true
			continue
		}

		// read data
		var data []byte
		if msg.GetDataLen() > 0 {
			data = make([]byte, msg.GetDataLen())
			if _, err := io.ReadFull(c.GetTCPConnection(), data); err != nil {
				fmt.Println("read msg data error ", err)
				c.ExitBuffChan <- true
				continue
			}
		}
		msg.SetData(data)
		req := Request{
			conn: c,
			msg:  msg,
		}
		if utils.GlobalObject.WorkerPoolSize > 0 {
			// 启动工作池机制，将消息交给Worker处理
			c.MsgHandle.SendMsgToTaskQueue(&req)
		} else {
			// 从绑定好的消息和对应的处理方法中执行对应的Handle方法
			go c.MsgHandle.DoMsgHandler(&req)
		}

	}
}

// 写消息Goroutine， 用户将数据发送给客户端
func (c *Connection) StartWrite() {
	fmt.Println("[Write Goroutine is running]")
	defer fmt.Println(c.RemoteAddr().String(), "[conn writer exist!]")
	for {
		select {
		case data := <-c.msgChan:
			// something to client
			if _, err := c.Conn.Write(data); err != nil {
				fmt.Println("send data error:", err, " conn write exit")
				return
			}
		case data, ok := <-c.msgBuffChan:
			if ok {
				if _, err := c.Conn.Write(data); err != nil {
					fmt.Println("send buff data error: ", err, " conn write exit")
					return
				}
			} else {
				fmt.Println("msgBuffChan is Closed")
				break
			}
		case <-c.ExitBuffChan:
			// exit
			return
		}
	}
}

// 启动连接
func (c *Connection) Start() {
	go c.StartReader()
	go c.StartWrite()
	c.TcpServer.CallOnConnStart(c)
	// instead of for select, Select is required, if you have multiple goroutines to handle.
	for range c.ExitBuffChan {
		// 得到退出消息，不再阻塞
		return
	}
}

// 关闭当前连接
func (c *Connection) Stop() {
	if c.isClosed {
		return
	}
	c.isClosed = true
	c.TcpServer.CallOnConnStop(c)
	err := c.Conn.Close()
	if err != nil {
		panic("close fail in connection")
	}
	// 通知关闭
	c.ExitBuffChan <- true
	c.TcpServer.GetConnMgr().Remove(c)
	// 关闭管道
	close(c.ExitBuffChan)
	close(c.msgBuffChan)
}

func (c *Connection) GetTCPConnection() *net.TCPConn {
	return c.Conn
}

// 获取当前连接ID
func (c *Connection) GetConnID() uint32 {
	return c.ConnID
}

func (c *Connection) RemoteAddr() net.Addr {
	return c.Conn.RemoteAddr()
}

// 发包
func (c *Connection) SendMsg(msgID uint32, data []byte) error {
	if c.isClosed {
		return errors.New("connection closed")
	}

	dp := NewDataPack()
	msg, err := dp.Pack(NewMsgPackage(msgID, data))
	if err != nil {
		fmt.Println("pack error msg id = ", msgID)
		return errors.New("pack error msg")
	}
	// write to client
	c.msgChan <- msg
	return nil
}

func (c *Connection) SendBuffMsg(msgID uint32, data []byte) error {
	if c.isClosed {
		return errors.New("connection closed")
	}
	dp := NewDataPack()
	msg, err := dp.Pack(NewMsgPackage(msgID, data))
	if err != nil {
		fmt.Println("pack error msg id = ", msgID)
		return errors.New("pack error msg")
	}
	c.msgBuffChan <- msg
	return nil
}

func (c *Connection) SetProperty(key string, value interface{}) {
	c.propertyLock.Lock()
	defer c.propertyLock.Unlock()
	c.property[key] = value
}

func (c *Connection) GetProperty(key string) (interface{}, error) {
	c.propertyLock.RLock()
	defer c.propertyLock.RUnlock()
	if value, ok := c.property[key]; ok {
		return value, nil
	} else {
		return nil, errors.New("no property found")
	}
}

func (c *Connection) RemoveProperty(key string) {
	c.propertyLock.Lock()
	defer c.propertyLock.Unlock()
	delete(c.property, key)

}
